package xin.nick.roc.consumer.client;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.AbstractApplicationContext;
import xin.nick.roc.consumer.anno.RpcClient;
import xin.nick.roc.consumer.proxy.RpcClientProxy;
import xin.nick.rpc.api.IUserService;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Nick
 * @date 2021/7/4
 * @description
 */
@Configuration
@Slf4j
public class MyRpcClientConfiguration implements ApplicationListener<ApplicationEvent> {

    private static List<ProviderInfo> providerInfoList = new ArrayList<>();

    private volatile AtomicBoolean isInit = new AtomicBoolean(false);

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {

        // 在执行 refresh 的时候,进行操作
        if ((applicationEvent instanceof ContextRefreshedEvent)) {
            //防止重复触发
            if(!isInit.compareAndSet(false,true)) {
                return;
            }
            try {
                // 在合适的生命周期下（ContextRefreshedEvent），进行远程对象的注入
                AbstractApplicationContext context = (AbstractApplicationContext) applicationEvent.getSource();
                injectionRemoteService(context);
            } catch (Exception e) {
            }
        }

    }

    /**
     * 注入远程服务的service
     * @param context
     */
    private void injectionRemoteService(AbstractApplicationContext context) {

        String[] beanNames = context.getBeanDefinitionNames();
        // 遍历所有的bean
        for (String name : beanNames) {
            Object service = context.getBean(name);
            Object target = null;

            // 找到最原本的对象
            try {
                target = getRealTarget(service);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }

            for (Field field : target.getClass().getDeclaredFields()) {
                field.setAccessible(true);
                String serviceName = field.getName();
                try {
                    // 没有RpcClient注解的不需要操作
                    if(!field.isAnnotationPresent(RpcClient.class)) {
                        continue;
                    };
                    // 如果原本容器里面没有该对象,则进行实例化存进去
                    if (!context.getBeanFactory().containsSingleton(serviceName)) {
                        log.info("injectionRemoteService serviceName : {}" , serviceName);
                        log.info("injectionRemoteService field : {}" , field.getType());
                        // 实例化远程对象
                        Object consumerBean = RpcClientProxy.createProxy(field.getType());
                        // 注册到Spring
                        context.getBeanFactory().registerSingleton(serviceName, consumerBean);
                    }

                    // 注入属性到bean
                    Object remoteBean = context.getBean(serviceName);
                    field.set(target, remoteBean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    break;
                } catch (Exception e) {
                    e.printStackTrace();
                    break;
                }

            }

        }

    }

    /**
     * 递归判断原本的对象
     * @param bean
     * @return
     * @throws Exception
     */
    public static Object getRealTarget(Object bean) throws Exception {
        if ((AopUtils.isAopProxy(bean)) && ((bean instanceof Advised))) {
            Advised advised = (Advised) bean;
            bean = advised.getTargetSource().getTarget();
            return getRealTarget(bean);
        }
        return bean;
    }
}
